/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.datasource.routing;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.tianyun.cloud.config.console.ConsoleBean;
import org.tianyun.cloud.datasource.configuration.DataSourceFactoryProperties;
import org.tianyun.cloud.datasource.hikari.HikariDataSourceBuilder;

import javax.sql.DataSource;
import java.io.Closeable;
import java.io.IOException;
import java.util.Properties;

/**
 * 动态路由数据源工厂
 *
 * @auther ebert_chan
 */
public class DataSourceFactory implements ConsoleBean, DisposableBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceFactory.class);

    private DataSourceFactoryProperties dataSourceFactoryProperties;

    private String name;

    private DataSource master;

    private DataSource slave;

    private boolean primary;

    public DataSourceFactory(String name, DataSourceFactoryProperties dataSourceFactoryProperties) throws IOException {
        this.dataSourceFactoryProperties = dataSourceFactoryProperties;
        this.setName(name);
        configure(dataSourceFactoryProperties);
    }

    private void configure(DataSourceFactoryProperties dataSourceFactoryProperties) throws IOException {
        Assert.notNull(dataSourceFactoryProperties.getMaster(), "Failed to create the datasource, the 'master' datasource is required");

        LOGGER.info("----------------------------------------------------------------------------------------------");
        LOGGER.info("Routing Datasource master: {}", StringUtils.hasText(dataSourceFactoryProperties.getMaster().getJdbcUrl()) ? dataSourceFactoryProperties.getMaster().getJdbcUrl().substring(0, dataSourceFactoryProperties.getMaster().getJdbcUrl().indexOf("?")) : "");
        if (dataSourceFactoryProperties.getSlave() != null) {
            LOGGER.info("Routing Datasource slave: {}", StringUtils.hasText(dataSourceFactoryProperties.getSlave().getJdbcUrl()) ? dataSourceFactoryProperties.getSlave().getJdbcUrl().substring(0, dataSourceFactoryProperties.getSlave().getJdbcUrl().indexOf("?")) : "");
        }
        if (dataSourceFactoryProperties.getPrimary()) {
            LOGGER.info("Routing Datasource primary: {}", dataSourceFactoryProperties.getPrimary());
        }
        LOGGER.info("----------------------------------------------------------------------------------------------");

        DataSource master = HikariDataSourceBuilder.configure(dataSourceFactoryProperties.getMaster(), dataSourceFactoryProperties.getProps()).wrapped().build();
        setMaster(master);
        setPrimary(dataSourceFactoryProperties.getPrimary() != null ? dataSourceFactoryProperties.getPrimary() : Boolean.FALSE);

        if (dataSourceFactoryProperties.getSlave() != null) {
            Boolean hasError = Boolean.FALSE;
            try {
                DataSource slave = HikariDataSourceBuilder.configure(dataSourceFactoryProperties.getSlave(), dataSourceFactoryProperties.getProps()).wrapped().build();
                setSlave(slave);
            } catch (Exception e) {
                hasError = Boolean.TRUE;
                throw e;
            } finally {
                if (hasError) {
                    try {
                        closeDataSource(getMaster());
                    } catch (Exception e) {
                        // ignore e
                    }
                }
            }
        }
    }

    @Override
    public Properties startupArguments() {
        Properties properties = new Properties();
        properties.put("datasource.master.jdbc-url", dataSourceFactoryProperties.getMaster().getJdbcUrl().substring(0, dataSourceFactoryProperties.getMaster().getJdbcUrl().indexOf("?")));
        if (dataSourceFactoryProperties.getSlave() != null) {
            properties.put("datasource.slave.jdbc-url", dataSourceFactoryProperties.getSlave().getJdbcUrl().substring(0, dataSourceFactoryProperties.getSlave().getJdbcUrl().indexOf("?")));
        }
        return properties;
    }

    // getter and setter

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public DataSource getMaster() {
        return master;
    }

    public void setMaster(DataSource master) {
        this.master = master;
    }

    public DataSource getSlave() {
        return slave;
    }

    public void setSlave(DataSource slave) {
        this.slave = slave;
    }

    public boolean isPrimary() {
        return primary;
    }

    public void setPrimary(boolean primary) {
        this.primary = primary;
    }

    @Override
    public void destroy() throws Exception {
        try {
            this.closeDataSource(getSlave());
        } finally {
            this.closeDataSource(getMaster());
        }
    }

    private void closeDataSource(DataSource dataSource) throws IOException {
        if (dataSource == null || !(dataSource instanceof Closeable)) {
            return;
        }
        Closeable closeable = (Closeable) dataSource;
        closeable.close();
    }

}
